package main;

import java.io.File;
import java.util.HashSet;
import java.util.Random;

import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.WorldCreator;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;

public class WorldManager {
	private final World overworld;
	private final World nether;
	private int radius, centerX, centerZ;
	private final Random random;
	
	public WorldManager(HashSet<String> lobby) {
		int worldId = 1;
		String worldName = "QUHC_" + worldId;
		while (Bukkit.getWorld(worldName) != null) {
			worldName = worldName.replaceFirst("" + worldId, "" + (worldId + 1));
			worldId = worldId + 1;
		}
		WorldCreator overworldCreator = new WorldCreator(worldName).generateStructures(true);
		WorldCreator netherCreator = new WorldCreator(worldName + "_nether").generateStructures(true);
		netherCreator.seed(overworldCreator.seed()).environment(Environment.NETHER);
		overworld = overworldCreator.createWorld();
		nether = netherCreator.createWorld();
		
		this.random = new Random();
		
		radius = lobby.size() * 45;
		centerX = overworld.getSpawnLocation().getBlockX();
		centerZ = overworld.getSpawnLocation().getBlockZ();
		
		int xLimit = lobby.size() * 48;
		int zLimit = lobby.size() * 48;
		int spawnX = overworld.getSpawnLocation().getChunk().getX();
		int spawnZ = overworld.getSpawnLocation().getChunk().getZ();
		
		for (Chunk chunk : overworld.getLoadedChunks()) {
			boolean checkX = Math.abs(chunk.getX() - spawnX) > xLimit;
			boolean checkZ = Math.abs(chunk.getZ() - spawnZ) > zLimit;
			if (checkX && checkZ) {
				chunk.unload(false, false);
			}
		}
		
		scatter(lobby);
	}

	private int flipAxis() {
		return random.nextBoolean()? 1 : -1;
	}
	
	private void scatter(HashSet<String> lobby) {
		Player previousPlayer = null;
		for (String name : lobby) {
			Player player = Bukkit.getPlayer(name);
			try {
				player.teleport(getStartingLocation());
				double minDistance = 1;
				while (player.getLocation().distance(previousPlayer.getLocation()) < (radius / minDistance)) {
					player.teleport(getStartingLocation());
					minDistance = minDistance + 0.1;
				}
				previousPlayer = player;
			}
			catch (Exception e) {
				player.teleport(getStartingLocation());
			}
		}
		
		for(Item item : overworld.getEntitiesByClass(Item.class)) {
			item.remove();
		}
		
		for (Player player : overworld.getPlayers()) {
			player.setHealth(20);
			player.setExp(0);
			player.setFoodLevel(20);
			player.setSaturation(20);
			player.getInventory().clear();
			player.getInventory().setArmorContents(null);	
			for (PotionEffect effect : player.getActivePotionEffects()) {
				player.removePotionEffect(effect.getType());
			}
		}
		
		generateBorders();
	}

	private Location generateLocation() {
		Location spawn = overworld.getSpawnLocation();
		int x = flipAxis() * random.nextInt(radius) + spawn.getBlockX();
		int z = flipAxis() * random.nextInt(radius) + spawn.getBlockZ();
		int y = overworld.getHighestBlockYAt(x, z);
		return new Location(overworld, x, y, z);
	}
	
	private Location getStartingLocation() {
		Spawnable spawnable = new Spawnable(generateLocation());
		while (spawnable.isValid() == false) {
			spawnable = new Spawnable(generateLocation());
		}
		return spawnable.getLocation();
	}

	public void generateBorders() {
		int wall = overworld.getPlayers().size() * 47;
		for (int i = -wall; i < wall; i++) {
			for (int y = 0; y < 255; y++) {
				overworld.getBlockAt(centerX + i, y, centerZ + wall).setType(Material.BEDROCK);
				overworld.getBlockAt(centerX + i, y, centerZ - wall).setType(Material.BEDROCK);
				overworld.getBlockAt(centerX + wall, y, centerZ + i).setType(Material.BEDROCK);
				overworld.getBlockAt(centerX - wall, y, centerZ + i).setType(Material.BEDROCK);
			}
		}
	}

	public World getOverworld() {
		return overworld;
	}

	public World getNether() {
		return nether;
	}

	public boolean getWorldIsEmpty() {
		return overworld.getPlayers().size() == 0 && nether.getPlayers().size() == 0;
	}

	public void deleteWorld() {		
		Bukkit.unloadWorld(overworld, true);
		Bukkit.unloadWorld(nether, true);
		deleteDirectory(overworld.getWorldFolder());
		deleteDirectory(nether.getWorldFolder());
	}

	private void deleteDirectory(File path) {
		if (path.exists()) {
			for (File file : path.listFiles()) {
				if (file.isDirectory()) {
					deleteDirectory(file);
				}
				file.delete();                  
			}
		}
		path.delete();
	}
}
